package cn.com.ry.framework.linklog.dubbo.filter;

import cn.com.ry.framework.linklog.constant.LinkLogConstants;
import cn.com.ry.framework.linklog.context.LinkLogContext;
import cn.com.ry.framework.linklog.context.SpanIdGenerator;
import cn.com.ry.framework.linklog.core.LinkLogLabel;
import cn.com.ry.framework.linklog.util.HostInfo;
import cn.com.ry.framework.linklog.util.LinkLogPrintUtil;
import cn.com.ry.framework.linklog.util.LinkLogSwitch;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.common.json.JSON;
import com.alibaba.dubbo.rpc.*;
import cn.com.ry.framework.linklog.core.LinkLogHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * dubbox的调用拦截器
 */
@Activate(group = {Constants.PROVIDER, Constants.CONSUMER}, order = -10000)
public class LinkLogDubboxFilter extends LinkLogHandler implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(LinkLogDubboxFilter.class);

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        Result result;
        String side = invoker.getUrl().getParameter(Constants.SIDE_KEY);

        if (side.equals(Constants.PROVIDER_SIDE)) {
            String preApp = invocation.getAttachment(LinkLogConstants.LINKLOG_PRE_APP_KEY);
            String preHost = invocation.getAttachment(LinkLogConstants.LINKLOG_PRE_HOST_KEY);
            String preIp = invocation.getAttachment(LinkLogConstants.LINKLOG_PRE_IP_KEY);
            String traceId = invocation.getAttachment(LinkLogConstants.LINKLOG_TRACE_KEY);
            String spanId = invocation.getAttachment(LinkLogConstants.LINKLOG_SPANID_KEY);

            LinkLogLabel labelBean = new LinkLogLabel(preApp, preHost, preIp, traceId, spanId);

            processProviderSide(labelBean);

            try {
                //打印请求及返回参数
                if (LinkLogSwitch.ACCESSLOG) {
                    buildAccessLog(LinkLogConstants.LINKLOG_LOG_CLIENT, invoker, invocation);
                }
                //调用dubbo
                result = invoker.invoke(invocation);
            } finally {
                cleanThreadLocal();
            }

            return result;
        } else if (side.equals(Constants.CONSUMER_SIDE)) {
            String traceId = LinkLogContext.getTraceId();

            if (traceId != null && !traceId.equals("")) {
                String appName = HostInfo.getAppName();
                String ip = HostInfo.getIp();
                String hostName = HostInfo.getHostName();
                RpcContext.getContext().setAttachment(LinkLogConstants.LINKLOG_TRACE_KEY, traceId);
                RpcContext.getContext().setAttachment(LinkLogConstants.LINKLOG_PRE_APP_KEY, appName);
                RpcContext.getContext().setAttachment(LinkLogConstants.LINKLOG_PRE_HOST_KEY, hostName);
                RpcContext.getContext().setAttachment(LinkLogConstants.LINKLOG_PRE_IP_KEY, ip);
                RpcContext.getContext().setAttachment(LinkLogConstants.LINKLOG_SPANID_KEY, SpanIdGenerator.generateNextSpanId());
            } else {
                logger.debug("[LinkLog]本地threadLocal变量没有正确传递traceId,本次调用不传递traceId");
            }
            //打印请求及返回参数
            if (LinkLogSwitch.ACCESSLOG) {
                buildAccessLog(LinkLogConstants.LINKLOG_LOG_CLIENT, invoker, invocation);
            }
            result = invoker.invoke(invocation);
        } else {
            result = null;
        }
        return result;
    }

    public void buildAccessLog(String accessType, Invoker<?> invoker, Invocation inv) {
        String group = invoker.getUrl().getParameter(Constants.GROUP_KEY);
        String serviceName = invoker.getInterface().getName();
        String version = invoker.getUrl().getParameter(Constants.VERSION_KEY);
        String method = inv.getMethodName();
        String param = "";
        String paramValue = "";
        StringBuilder paramsSB = new StringBuilder();
        Class<?>[] types = inv.getParameterTypes();
        if (types != null && types.length > 0) {
            boolean first = true;
            for (Class<?> typeNode : types) {
                if (first) {
                    first = false;
                } else {
                    paramsSB.append(",");
                }
                paramsSB.append(typeNode.getName());
            }
        }
        param = paramsSB.toString();
        Object[] args = inv.getArguments();
        if (args != null && args.length > 0) {
            try {
                paramValue = JSON.json(args);
            } catch (IOException e) {
                logger.error("JSON格式转换失败", e);
            }
        }

        if (paramValue != null && paramValue.length() > LinkLogConstants.maxContentLength) {
            return;
        }
        Map map = new HashMap();
        map.put("linkLogAccessType", accessType);
        map.put("group", group);
        map.put("requestURLStr", serviceName);
        map.put("version", version);
        map.put("requestURI", method);
        map.put("param", param);
        map.put("method", "DUBBO");

        LinkLogPrintUtil.printRequestLog(paramValue, LinkLogDubboxFilter.class.getName(), map);
    }

}
